docs(website): re-shoot the docs stills on the 0.69 UI, and make the rig reproducible - #828
Closed
edspencer wants to merge 7 commits into
Closed
docs(website): re-shoot the docs stills on the 0.69 UI, and make the rig reproducible#828edspencer wants to merge 7 commits into
edspencer wants to merge 7 commits into
Conversation
added 5 commits
August 10, 2026 13:49
root-home, adopt-row, adopt-modal, project-settings and the trigger tool picker were all shot before the design work landed (#763/#768/#769/#780/#801). Every one showed chrome, surfaces, type and accent the redesign replaced, on pages whose prose is in the present tense. Shot from the docs-media rig at v0.69.0, Foundation/dark, hue null, tint 0 — the out-of-the-box appearance. capture.mjs now pins the theme via addInitScript (the keys are read by a pre-paint script, so a post-goto evaluate catches a mid-swap frame) and asserts it applied.
… panel + theme quartet Foundation dark at the out-of-box default (theme quartet excepted). Shot against a rebuilt rig at main, verified post-redesign via the Appearance section. Discover is NOT included: the rig has no discoverable Claude Code history, so the route renders its empty state. Needs seeding.
The previous frame caught the list when tidepool held a single chat, so it showed the adopted row alone — no contrast, which is the entire point of the image on using/working-in-chats.md:80. Re-shot with four ordinary chats above it, so the terminal provenance marker reads AS a distinction.
Deploying paddock with
|
| Latest commit: |
f60fe9d
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://e58fed9d.paddock-7u2.pages.dev |
| Branch Preview URL: | https://docs-media-v069.paddock-7u2.pages.dev |
added 2 commits
August 10, 2026 14:06
…shot Four runtime themes plus a free accent picker make "which theme is this?" unanswerable from a PNG — which is most of what made this re-shoot expensive to reason about. capture.mjs now writes <shot>.png.json observed FROM THE LIVE PAGE (theme, light/dark, hue, tint, solved accent, app version, route, viewport), so a silently-failed theme is detectable after the fact rather than only at capture time. shots.manifest.json is the committed aggregate. Immediately useful: the quartet records four distinct accent triples, which is what proves the theme applied rather than just the label being written.
The re-shot frame names a different chat and shows four ordinary rows above the adopted one; the alt text still described the previous capture.
edspencer
pushed a commit
that referenced
this pull request
Aug 10, 2026
git rm --cached was not enough: it left the files in the working tree, where the next blanket `git add -A` re-staged them and silently undid the split. Removing them from the tree on this branch is what actually holds. They are committed on docs/media-v069 (#828), which is where #815 should take them from — an asset and the reference to it belong in one PR, and whats-new.mdx is on #815.
Owner
Author
edspencer
pushed a commit
that referenced
this pull request
Aug 10, 2026
This was referenced Aug 10, 2026
edspencer
added a commit
that referenced
this pull request
Aug 11, 2026
…830) * docs(docs-media): document the rig contract, sidecars and the leak-pattern rule * chore(docs-media): commit the rig launcher, seed and fixtures seed.mjs and serve.sh existed only on one box and were full of its paths, ports and process-manager specifics, which made tools/docs-media/ unusable by anyone else. Both are now driven by PADDOCK_RIG_{HOME,CLONE,PROJECTS,FIXTURES}. serve.sh was RETYPED from a spec rather than copied: the original held a live OAuth token in plaintext, and a copy keeps the credential in the editor buffer and in shell history. It carries no credential and cannot: it re-execs under env -i, and the rig runs a fake claude on the batch runtime. capture.mjs gains theme pinning via addInitScript (the appearance keys are read by a pre-paint script, so writing them after goto shoots mid-swap) and the provenance sidecars. * docs(website): re-shoot the stale stills on the 0.67+ UI Every UI-bearing image on the site predated the redesign, including the seven committed the previous pass — those were merged 41 minutes before the first design commit, so age was never the criterion. Foundation dark at the out-of-box default (DEFAULT_APPEARANCE; paddock:theme defaults to dark), which is what a reader sees on first boot. The theme quartet is the deliberate exception: there the choice itself is the subject. * docs(runbook): correct the 127.0.0.1 baseline split (39 docs + 7 README = 46) The previous paragraph attributed all 46 to the docs subtree and then added "plus 6 in README.md", summing to 52. A baseline that overcounts is how a genuine new hit hides inside an expected one. * docs(runbook): what a DESIGN release changes about a docs pass Four things this pass proved the runbook lacked, all in section 5: - The stale-build trap gets its own warning. Serving dist/ from a checkout that predates the release means every re-shot frame is the old UI, and it fails SILENTLY: rig up, seed passes, leak scan passes, every shot succeeds, md5sum clean. Recorded as a positive check against the SERVED BUNDLE — name a UI element only the new build can paint, then go and look at it — rather than against the git state of a directory. - Bucket by the TENSE of the surrounding prose, not the directory. A What's New entry records a release as it shipped, so an old-UI frame there is correct and re-shooting it falsifies the archive; a current-behaviour page showing obsolete UI is simply wrong. Directory is a proxy that fails on exactly the dual-use assets cited from both — fork those. - The rig's own fragility. A projects root on a non-persisted volume loses the whole tree on a restart while the data dir survives, so the instance boots to zero projects plus orphaned job records: it does not fail, it lies. Wipe both or neither, and commit the launcher and seed so the rig is reproducible. - Appearance is no longer determined by the URL. Pin the theme with addInitScript (the keys are read pre-paint), clear the solved-accent cache, assert it applied, and never verify a theme by grepping CSS. Plus the seeding race (rename in a second pass or the transcript's own title resolution clobbers it) and the md5sum/framing discipline for near-identical shots. Box paths, ports and domains are written as placeholders. * docs(runbook): pinning the appearance, and seeding a rig with texture Runtime themes mean a screenshot is no longer determined by the URL. The keys are read by a pre-paint inline script, so a capture must write them with addInitScript rather than page.evaluate after goto (which yields a mid-swap frame), must clear the solved-accent cache (keyed theme:mode — a stale entry paints the previous theme's accent pre-boot), and should assert it applied. Never verify a theme by grepping CSS: OKLCH serialises as oklch(...) and the accent token is a bare RGB triple, so a regex reader scores a themed build zero. Also the seeding race: a chat is the product of a turn, so an API-only seed photographs as an empty app; and renaming a chat immediately after the turn completes loses to the transcript's own title resolution. * docs(website): remove the whats-new stills from the captures PR git rm --cached was not enough: it left the files in the working tree, where the next blanket `git add -A` re-staged them and silently undid the split. Removing them from the tree on this branch is what actually holds. They are committed on docs/media-v069 (#828), which is where #815 should take them from — an asset and the reference to it belong in one PR, and whats-new.mdx is on #815. * chore: drop the captures from this PR — they ship in #829 The stills were pushed onto this branch by a concurrent worker. This PR is the rig and the tooling, which is code-shaped and independently reviewable; the frames it produces are a separate review with separate criteria. * docs(runbook): negative findings must be re-verified by the relaying party The most valuable thing this pass produced. A wrong positive claim is caught by the next reader; a wrong negative is acted on by DELETING the evidence — correct prose is removed and it reads as diligence. Happened twice within an hour in opposite directions over one file: the accent picker's floor was reported unenforced (wrong — repairFill does repair derived tokens at runtime), then over-corrected into "nothing is enforced" (also wrong), neither time by anyone who opened accent.ts. The rule therefore binds the corrector too, and negatives must be scoped to their narrowest true form. Records the control that actually worked: the issue-filing child was told to re-verify against main before filing, did, and its issues carry the narrow true claim while the broken version was still circulating above it. * docs: drop the runbook from this PR — #833 owns it The same 216-line diff is in #833. Two open PRs editing one 760-line file conflict on whichever merges second, and this PR is tooling: tools/docs-media/** is independently reviewable and independently revertable without it. * chore(docs-media): drop the manifest entry for a shot that ships nowhere docs-discover.png was captured and deliberately not committed: the rig has no discoverable Claude Code history, so the route renders its empty state. A manifest entry for a frame that exists in no PR invites someone to go looking for it. The twelve remaining entries all ship — seven in #829 and five in #815. Those five are NOT pruned despite having moved out of #829: they still ship, and the manifest is the only record of which theme and solved accent each was shot at, which is exactly what a reviewer of #815 needs. * docs(docs-media): restore the README the rig PR is supposed to carry Building this branch with 'git checkout <other-branch> -- tools/docs-media' took the README from that branch too, silently reverting the rig-contract and sidecar sections to main's 69-line version — so the PR shipped the scripts with none of the documentation that makes them usable, which was half the point. Also adds the staleness note: because each sidecar records the app version, 'is this frame stale after a repaint?' is a file read rather than an inspection of every PNG. * docs(docs-media): note the uniform-timestamp tell as a known seed improvement seed.mjs creates its chats in one run, so every row in a capture carries the same relative age — the v0.69 Home frame reads '2h ago' nine times. That is the hand-seeded-fixture tell the runbook already warns about; a real instance has this morning's work beside last week's. The adoptable sessions already back-date their mtimes; the chats do not. Recorded as a known improvement, not a fix: no committed frame is wrong because of it. * chore(docs-media): make HOME overridable, and actually forward it On Discover the home path IS the content: DiscoverView renders {candidate.path} and {result.homeDir} verbatim into <code>. So cropping fails (the path is the subject), masking fails (the leak-masker would blank the element being shot), and a symlink fails (paddock canonicalises for display). A rig whose HOME sits under a scratch directory cannot produce a publishable Discover frame at all — the shot is lost at launch, not at capture. The override was inert as first written: env -i wipes the environment before the re-exec, and PADDOCK_RIG_USER_HOME was not in the forwarding list, so the second pass always fell back to the default. Same for PADDOCK_RIG_FIXTURES, which the README documented and the launcher could never receive. Both now forwarded, and verified with a control: without the override HOME resolves to <rig>/home, with it to the path given. Also documents the no-recorded-cwd exclusion — a staged transcript without a cwd is dropped silently, which is the most common way a Discover seed fails while looking like it worked. * fix(docs-media): make HOME overridable, or Discover can never be shot serve.sh hard-coded HOME to a scratch directory. On the Discover screen the home path IS the content: DiscoverView renders {candidate.path} and {result.homeDir} verbatim in <code>, with no truncation. So that rig cannot produce a publishable Discover frame, and every escape is blocked — cropping loses the subject, the leak-masker would blank the subject, and a symlink is defeated by paddock canonicalising for display. The reason is in the comment because the reason is what stops someone simplifying it back. Also implements PADDOCK_RIG_FIXTURES, which the README documented and serve.sh never had — so the committed fixtures.json was a dead file and the authored replies never reached the fake claude. Both new vars are threaded through the env -i re-exec; without that the override is scrubbed and silently does nothing. Documents the other Discover trap: a staged transcript with no cwd is excluded as no-recorded-cwd, which fails while looking like it worked. --------- Co-authored-by: HomeLab Agent <homelab-infra@valfenda.net>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Every UI-bearing asset on the site was shot before the design work landed. The
seven stills merged in the previous pass predate the first design commit (
67e493f)by 41 minutes, so they are pre-redesign exactly like the older ones. Age was never the
criterion — three of these were a day old and still wrong, because of what landed
after they were committed.
These sit under prose in the present tense, on pages a reader consults to operate
the product. A pre-redesign frame there is worse than no image: a reader who cannot
find the pictured control concludes the docs are stale everywhere.
Re-shot — 7 stills
getting-started/root-home.png·config/project-settings.png·using/adopt-row.png·using/adopt-modal.png·using/adopted-badge.png·using/promote-to-project.png·guides/trigger-tool-picker-bash.pngAll at v0.69.0 in Foundation / dark /
hue: null/tint: 0— the out-of-the-boxappearance (
DEFAULT_APPEARANCE;paddock:themedefaults to dark). That is what areader sees on first boot, which is a docs screenshot's whole job.
Two alt-text corrections ride along, and they are not cosmetic — alt text is the
only description a screen-reader user gets.
getting-started.mdclaimed four unreadchats and named OVERVIEW.MD/CHANGELOG.MD sections; the frame shows nine unread, three
projects grouped by area, and RUNNING/UNREAD/FILES.
Not here: the theme quartet and the Appearance panel
Those five stills live in #815, which also carries the 0.67 prose that references
them. Keeping a copy here would have made them orphans on this branch and put the same
bytes in two PRs. This branch has zero orphans and zero dangling references —
checked mechanically, both directions.
Making the rig reproducible
seed.mjsandserve.shexisted only on one machine and were full of its paths andports — so
capture.mjs, which is committed, was unusable by anyone else. Both arenow committed, driven by four env vars (
PADDOCK_RIG_HOME/_CLONE/_PROJECTS/_BASE), with no box paths.Worth reviewing rather than skimming:
serve.shwas retyped from a spec, not copied. The original held a live OAuthtoken in plaintext; those lines are gone rather than edited. A rig running a fake
claudemust never need a credential./home. The previous rig kept it there on a boxwhere only
/datawas a volume — a container restart destroyed everyproject.yamland every
.chats/*.jsonlwhile the data dir survived, leaving orphaned job recordsreporting chats whose transcripts were gone. Default is now
$PADDOCK_RIG_HOME/projects.0.0.0.0. WithPADDOCK_AUTH_MODE=none, binding allinterfaces publishes an unauthenticated instance. Capture runs on the same host, so
PADDOCK_DANGEROUSLY_ALLOW_OPENis no longer needed at all.capture.mjspins the theme viaaddInitScriptand asserts it applied. It hasto be
addInitScript: the threepaddock:*keys are read by a pre-paint inlinescript, so a
page.evaluateaftergotocatches a mid-swap frame — and a stalepaddock:appearance-cachepaints the previous theme's accent for one frame.shots.manifest.jsonrecords the observed theme, accent, mode and app versionbeside every shot, so "which build is this frame from?" is a file read rather than
the forensics that made this pass expensive.
Verification
cd website && npm install && npm run build→ exits 0, 52 pages.127.0.0.1hits across
website/src/content/docs/**+README.md, matching the recountedbaseline. No private hostname, LAN IP, container id or box path in the diff.
/api/instance-config(dataDir+driveMode: batch),never from
pm statusor/api/health— both are satisfied by a stale squatter.mainand/configconfirmed torender the Appearance section, which cannot exist before
75c3935. Without thisevery "re-shot" frame would be the old UI again, and nothing else would show it.
md5sumon every shot — no byte-identical pairs. Two unframed shots of one URLat one viewport land as the same file.
strings shot.pngis not a leakcheck — rendered text is pixels.
Docs-only: no changeset, no version bump.
Not in this PR
and the dual-use forks.
docs/demo/paddock-demo.gifand itswebsite/public/demo/copy — owned by docs(demo): re-shoot the demo reel on main at v0.67.0 (supersedes #790) #797.